﻿using JGSY.SerialCommunication.UpperComputer.Core;
using JGSY.SerialCommunication.UpperComputer.IComponent;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace JGSY.SerialCommunication.UpperComputer.Component
{
    /// <summary>
    /// 以太网通信组件 - 支持TCP/UDP协议，优化超时和重试机制
    /// </summary>
    public class Ethernet : ICommunicationUnit, IDisposable
    {
        // 连接配置参数
        private readonly string _ipAddress;         // 目标IP地址
        private readonly int _port;                 // 目标端口号
        private readonly ProtocolType _protocolType; // 协议类型（TCP或UDP）
        private readonly int _defaultReceiveTimeout; // 默认接收超时时间（毫秒）
        private readonly int _defaultConnectTimeout; // 默认连接超时时间（毫秒）

        // 通信状态
        private Socket _socket;                     // 底层Socket对象
        private bool _disposed;                     // 对象是否已释放
        private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1); // 连接操作锁
        private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource(); // 释放操作令牌
        private readonly bool _isUdp;               // 是否为UDP协议
        private EndPoint _remoteEndPoint;           // UDP远程端点

        // 可配置的连接超时时间（动态修改）
        public int ConnectTimeOut { get; set; }

        /// <summary>
        /// 初始化以太网通信组件
        /// </summary>
        /// <param name="ipAddress">目标IP地址</param>
        /// <param name="port">目标端口号（1-65535）</param>
        /// <param name="receiveTimeout">默认接收超时时间(毫秒)，最小1000ms</param>
        /// <param name="connectTimeout">默认连接超时时间(毫秒)，最小1000ms</param>
        /// <param name="protocolType">协议类型（TCP/UDP）</param>
        public Ethernet(string ipAddress, int port, int receiveTimeout = 10000, int connectTimeout = 5000, ProtocolType protocolType = ProtocolType.Tcp)
        {
            // 参数验证
            if (string.IsNullOrEmpty(ipAddress))
                throw new ArgumentNullException(nameof(ipAddress));
            if (!IPAddress.TryParse(ipAddress, out _))
                throw new ArgumentException("无效的IP地址格式", nameof(ipAddress));
            if (port < 1 || port > 65535)
                throw new ArgumentOutOfRangeException(nameof(port), "端口号必须在1-65535之间");

            // 超时参数限制（避免过短超时）
            _defaultReceiveTimeout = Math.Max(receiveTimeout, 1000);
            _defaultConnectTimeout = Math.Max(connectTimeout, 1000);

            _ipAddress = ipAddress;
            _port = port;
            _protocolType = protocolType;
            _isUdp = protocolType == ProtocolType.Udp;
            ConnectTimeOut = _defaultConnectTimeout;
        }

        /// <summary>
        /// 打开网络连接（异步）
        /// </summary>
        /// <param name="timeout">连接超时时间(毫秒)，默认使用配置值</param>
        /// <returns>操作结果（成功/失败及消息）</returns>
        public async Task<Result<bool>> OpenAsync(int timeout = -1)
        {
            if (_disposed)
                return new Result<bool> { Status = false, Message = "对象已释放" };

            // 超时参数处理（确保最小值）
            int actualTimeout = timeout > 0 ? Math.Max(timeout, 1000) : _defaultConnectTimeout;

            await _connectionLock.WaitAsync(_disposeCts.Token).ConfigureAwait(false);
            try
            {
                if (IsSocketConnected())
                    return new Result<bool> { Status = true, Message = "已连接" };

                // 关闭现有连接
                SafeCloseSocket();

                // 创建Socket
                _socket = new Socket(
                    AddressFamily.InterNetwork,
                    _isUdp ? SocketType.Dgram : SocketType.Stream,
                    _protocolType
                );

                // 基础配置
                _socket.ReceiveTimeout = _defaultReceiveTimeout;
                _socket.SendTimeout = _defaultReceiveTimeout;

                // UDP特殊配置
                if (_isUdp)
                {
                    _socket.EnableBroadcast = true;
                    _remoteEndPoint = new IPEndPoint(IPAddress.Parse(_ipAddress), _port);
                }

                // 连接超时控制
                using var cts = CancellationTokenSource.CreateLinkedTokenSource(_disposeCts.Token);
                cts.CancelAfter(actualTimeout);

                try
                {
                    // 异步连接
                    var connectTask = _socket.ConnectAsync(IPAddress.Parse(_ipAddress), _port);
                    var completedTask = await Task.WhenAny(connectTask, Task.Delay(actualTimeout, cts.Token)).ConfigureAwait(false);

                    if (completedTask != connectTask)
                        throw new TimeoutException($"连接超时（{actualTimeout}ms）");

                    await connectTask.ConfigureAwait(false); // 等待完成并传播异常
                    LogDebug($"连接成功: {_ipAddress}:{_port}");
                    return new Result<bool> { Status = true, Message = "连接成功" };
                }
                catch (OperationCanceledException)
                {
                    throw new TimeoutException($"连接超时（{actualTimeout}ms）");
                }
                catch (Exception ex)
                {
                    LogError("连接失败", ex);
                    SafeCloseSocket();
                    return new Result<bool> { Status = false, Message = $"连接失败: {ex.Message}" };
                }
            }
            finally
            {
                _connectionLock.Release();
            }
        }

        /// <summary>
        /// 发送数据并接收响应（异步，带重试机制）
        /// </summary>
        /// <param name="request">请求数据（非空）</param>
        /// <param name="receiveLen">预期接收长度（字节）</param>
        /// <param name="errorLen">错误响应长度（字节，0表示不检查）</param>
        /// <param name="timeout">接收超时时间(毫秒)，默认使用配置值</param>
        /// <returns>响应结果（状态、数据、消息）</returns>
        public async Task<Result<byte>> SendAndReceiveAsync(List<byte> request, int receiveLen, int errorLen = 0, int timeout = -1)
        {
            const int maxRetries = 3;
            int actualTimeout = timeout > 0 ? Math.Max(timeout, 5000) : _defaultReceiveTimeout;

            if (_disposed)
                return new Result<byte> { Status = false, Message = "对象已释放" };
            if (request == null || !request.Any())
                return new Result<byte> { Status = false, Message = "请求数据不能为空" };
            if (receiveLen < 0)
                return new Result<byte> { Status = false, Message = "预期接收长度不能为负数" };

            for (int retry = 0; retry < maxRetries; retry++)
            {
                try
                {
                    if (!IsSocketConnected())
                    {
                        LogDebug($"第 {retry + 1} 次尝试：连接断开，正在重连");
                        if (!await AutoReconnectAsync())
                            continue;
                    }

                    LogDebug($"第 {retry + 1} 次发送：{BitConverter.ToString(request.ToArray())}");

                    int bytesSent = _isUdp
                        ? await _socket.SendToAsync(request.ToArray(), SocketFlags.None, _remoteEndPoint)
                        : await _socket.SendAsync(request.ToArray(), SocketFlags.None);

                    if (bytesSent != request.Count)
                        throw new SocketException((int)SocketError.MessageSize);

                    var buffer = new byte[4096];
                    var response = new List<byte>();
                    var startTime = DateTime.UtcNow;

                    while (true)
                    {
                        // 手动检查超时
                        if ((DateTime.UtcNow - startTime).TotalMilliseconds > actualTimeout)
                            throw new TimeoutException($"接收超时（{actualTimeout}ms）");

                        if (response.Count >= receiveLen || (errorLen > 0 && response.Count == errorLen))
                            break;

                        // 使用无取消令牌的重载
                        int bytesRead =   _socket.Receive(buffer, SocketFlags.None);
                        if (bytesRead == 0)
                            throw new SocketException((int)SocketError.ConnectionReset);

                        response.AddRange(buffer.Take(bytesRead));
                        LogDebug($"已接收 {response.Count}/{receiveLen} 字节");
                    }

                    bool isSuccess = response.Count >= receiveLen || (errorLen > 0 && response.Count == errorLen);
                    string message = isSuccess
                        ? (response.Count >= receiveLen ? "接收成功" : "接收错误响应")
                        : $"接收不完整（预期{receiveLen}，实际{response.Count}）";

                    LogDebug($"第 {retry + 1} 次接收：{message}");
                    return new Result<byte> { Status = isSuccess, Datas = response, Message = message };
                }
                catch (TimeoutException ex)
                {
                    LogError($"第 {retry + 1} 次接收超时", ex);
                    SafeCloseSocket();
                    if (retry == maxRetries - 1)
                        return new Result<byte> { Status = false, Message = ex.Message };
                }
                catch (SocketException ex)
                {
                    LogError($"第 {retry + 1} 次Socket错误：{ex.SocketErrorCode}", ex);
                    SafeCloseSocket();
                    if (retry == maxRetries - 1)
                        return new Result<byte> { Status = false, Message = $"Socket错误：{ex.Message}" };
                }
                catch (Exception ex)
                {
                    LogError($"第 {retry + 1} 次尝试异常：{ex.Message}", ex);
                    SafeCloseSocket();
                    if (retry == maxRetries - 1)
                        return new Result<byte> { Status = false, Message = $"通信异常：{ex.Message}" };
                }

                int delayMs = Math.Min(500 * (1 << retry), 3000);
                await Task.Delay(delayMs).ConfigureAwait(false);
            }

            return new Result<byte> { Status = false, Message = "所有重试均失败" };
        } 

        /// <summary>
        /// 自动重连机制
        /// </summary>
        /// <returns>重连成功返回true</returns>
        private async Task<bool> AutoReconnectAsync()
        {
            const int maxReconnectAttempts = 2; // 重连尝试次数
            for (int i = 0; i < maxReconnectAttempts; i++)
            {
                try
                {
                    LogDebug($"自动重连第 {i + 1} 次");
                    var result = await OpenAsync().ConfigureAwait(false);
                    if (result.Status)
                        return true;
                    await Task.Delay(100 * (i + 1)).ConfigureAwait(false); // 退避延迟
                }
                catch (Exception ex)
                {
                    LogError($"自动重连第 {i + 1} 次失败", ex);
                }
            }
            return false;
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            if (_disposed) return;

            _connectionLock.Wait();
            try
            {
                if (_socket != null)
                {
                    LogDebug("关闭连接");
                    SafeCloseSocket();
                }
            }
            finally
            {
                _connectionLock.Release();
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (_disposed) return;

            _disposed = true;
            LogDebug("释放资源");

            // 取消所有异步操作
            _disposeCts.Cancel();
            _disposeCts.Dispose();

            // 关闭连接
            Close();

            // 释放同步锁
            _connectionLock.Dispose();
        }

        /// <summary>
        /// 检查Socket连接状态
        /// </summary>
        private bool IsSocketConnected()
        {
            if (_socket == null) return false;
            if (!_socket.Connected) return false;

            // UDP无连接状态，只要未释放即为可用
            if (_isUdp) return !_disposed;

            // TCP需要额外检查（通过Poll方法）
            bool isDisconnected = _socket.Poll(1000, SelectMode.SelectRead) && _socket.Available == 0;
            if (isDisconnected)
            {
                try
                {
                    // 尝试非阻塞读取，验证连接状态
                    _socket.Receive(new byte[1], SocketFlags.Peek);
                }
                catch (SocketException)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 安全关闭Socket（忽略异常）
        /// </summary>
        private void SafeCloseSocket()
        {
            try
            {
                if (_socket != null)
                {
                    _socket.Shutdown(SocketShutdown.Both);
                    _socket.Close();
                    _socket.Dispose();
                }
            }
            catch (Exception ex)
            {
                LogError("关闭Socket时发生异常", ex);
            }
            finally
            {
                _socket = null;
            }
        }

        /// <summary>
        /// 记录错误日志
        /// </summary>
        private void LogError(string message, Exception ex = null, List<byte> request = null)
        {
            var log = new StringBuilder();
            log.AppendLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ERROR] {message}");

            if (ex != null)
            {
                log.AppendLine($"异常类型: {ex.GetType().FullName}");
                log.AppendLine($"异常消息: {ex.Message}");
                log.AppendLine($"堆栈跟踪: {ex.StackTrace}");
            }

            if (request != null && request.Count > 0)
            {
                log.AppendLine($"请求数据: {BitConverter.ToString(request.ToArray())}");
            }

            Debug.WriteLine(log.ToString());
        }

        /// <summary>
        /// 记录调试日志
        /// </summary>
        private void LogDebug(string message)
        {
            Debug.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [DEBUG] {message}");
        }

        // 显式实现同步接口（内部调用异步方法）
        Result<bool> ICommunicationUnit.Open(int timeout)
        {
            return OpenAsync(timeout).GetAwaiter().GetResult();
        }

        Result<byte> ICommunicationUnit.SendAndRecevice(List<byte> request, int receiveLen, int errorLen)
        {
            return SendAndReceiveAsync(request, receiveLen, errorLen).GetAwaiter().GetResult();
        }
    }
}